"""
media_storage.interfaces
========================

All application-exposed interface definitions are collected here, allowing
future maintainers to quickly understand what functionalities their would-be
concrete classes must implement.

Usage
-----

This module is not meant to be used externally.

Legal
-----

This file is part of the LGPLed Python client of the media-storage project.
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU General Public License and
GNU Lesser General Public License along with this program. If not, see
<http://www.gnu.org/licenses/>.
 
(C) Neil Tallim, 2011
"""
from abc import ABCMeta, abstractmethod

import compression

class BaseConstruct(object):
    """
    Defines methods that must be implemented by all clients.
    """
    __metaclass__ = ABCMeta
    
    @abstractmethod
    def ping(self, timeout=1.0):
        """
        Pings the server to indicate whether it is alive or not; an exception is raised on error.
        
        `timeout` is the number of seconds to wait for a response.
        """
        
class StorageConstruct(BaseConstruct):
    """
    Defines methods that must be implemented to allow data to be stored by a client.
    """
    __metaclass__ = ABCMeta
    
    @abstractmethod
    def put(self, data, mime, family=None,
     comp=compression.COMPRESS_NONE, compress_on_server=False,
     deletion_policy=None, compression_policy=None,
     meta=None,
     uid=None, keys=None,
     timeout=10.0
    ):
        """
        Stores data in the environment.
        
        `data` is free-form and every implementing class may choose to handle it differently.
        
        `family`, which is optional (defaulting to `None`, meaning "generic"), can be used to store
        data in different filesystems or, more simply, as a means of logically separating data in
        queries.
        
        `comp` is the type of compression to be applied, as one of the compression type
        constants (defaulting to uncompressed); `compress_on_server` indicates whether compression
        should happen locally (recommended in most cases) or on the server (which will require an
        appropriate timeout value).
        
        `deletion_policy` may either be `None` or an empty dictionary, which means the file is never
        deleted (default) or a dictionary containing one or both of the following:
         - 'fixed': The number of seconds to retain the file from the time it was uploaded
         - 'stale': The number of seconds that must elapse after the file was last downloaded to
                    qualify it for deletion
                    
        `compression_policy` is the same as `deletion_policy`, only with one extra element:
         - 'comp': Any of the compression type constants, except for none, which disables the
                   policy; this will cause the data to be (re)compressed in that format when either
                   condition is met.
        
        `meta` is a dictionary (or `None`) containing any metadata to be used to identify the
        uploaded content through querying. All scalar value-types are supported.
        
        If not implementing a proxy, do not pass anything for `uid` or otherwise pick something that
        has no chance of colliding with a UUID(1).
        
        In general, you should not need to specify anything for `keys`, but if you have a homogenous
        or anonymous access policy, it is a dictionary containing the elements 'read' and 'write',
        both strings or `None`, with `None` granting anonymous access to the corresponding facet.
        Either element may be omitted to have it generated by the server.
        
        `timeout` has a different default depending on the implementation, but is always the number
        of seconds to wait for a response.
        """
        
class RetrievalConstruct(BaseConstruct):
    """
    Defines methods that must be implemented to allow data to be retrieved by a client.
    """
    __metaclass__ = ABCMeta
    
    @abstractmethod
    def get(self, uid, read_key, output_file=None, decompress_on_server=False, timeout=5.0):
        """
        Retrieves the requested data from the environment.
        
        `uid` and `read_key` are used to access the requested data.
        
        `output_file` is a free-form specifier that implementations may use to allow the caller to
        indicate where retrieved content should go; it is not defined at this stage.
        
        `decompress_on_server` ensures that the server will handle decompression, though this also
        happens if the client doesn't support the compression in use. This should be left off, which
        is the default, unless necessary.
        
        `timeout` is the number of seconds to allow for retrieval to complete, with default values
        left up to the implementing class; if decompressing, it should be increased accordingly.
        """
        
    @abstractmethod
    def describe(self, uid, read_key, timeout=2.5):
        """
        Retrieves the requested record from the environment as a dictionary.
        
        `uid` and `read_key` are used to access the requested data.
        
        `timeout` is the number of seconds to allow for retrieval to complete.
        """
        
        
class ControlConstruct(StorageConstruct, RetrievalConstruct):
    """
    Defines methods that must be implemented to allow data to be manipulated by a client.
    
    In its current form, also inherits storage and retreival methods, since there's no obvious
    case for allowing independent manuipulation.
    """
    __metaclass__ = ABCMeta
    
    @abstractmethod
    def list_families(self, timeout=2.5):
        """
        Enumerates all families currently defined on the server, returning a sorted list of strings.
        
        `timeout` is the number of seconds to allow for retrieval to complete.
        """
        
    @abstractmethod
    def unlink(self, uid, write_key, timeout=2.5):
        """
        Unlinks the identified data from the environment.
        
        `uid` and `write_key` are used to access the requested data.
        
        `timeout` is the number of seconds to allow for unlinking to complete.
        """
        
    @abstractmethod
    def update(self, uid, write_key,
     new={}, removed=(),
     deletion_policy=None, compression_policy=None,
     timeout=2.5
    ):
        """
        Updates attributes of an existing record in the environment.
        
        `uid` and `write_key` are used to access the requested data.
        
        `new` is a dictionary of meta-data that will be used to update (add and replace) existing
        meta-data. `removed` is a collection of meta-data keys to be dropped. By default, both are
        empty.
        
        `deletion_policy` is either `None`, which effects no change (the default) or a dictionary,
        of the same form as in `put()`, that replaces the current policy. `compression_policy`
        follows the same rules.
        
        `timeout` is the number of seconds to allow for updating to complete.
        """
        
    @abstractmethod
    def query(self, query, timeout=5.0):
        """
        Given a ``common.QueryStruct`` as `query`, a list of all matching records are retrieved from
        the environment, subject to implementation limits.
        
        `timeout` is the number of seconds to wait for a response.
        """
        
